﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.MapEvents.MapEvent
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.ComponentInterfaces;
using TaleWorlds.CampaignSystem.Encounters;
using TaleWorlds.CampaignSystem.Map;
using TaleWorlds.CampaignSystem.Party;
using TaleWorlds.CampaignSystem.Roster;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.CampaignSystem.Siege;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.ObjectSystem;
using TaleWorlds.SaveSystem;
using TaleWorlds.SaveSystem.Load;

#nullable disable
namespace TaleWorlds.CampaignSystem.MapEvents
{
  public sealed class MapEvent : MBObjectBase, IMapEntity
  {
    private const float BattleRetreatMinimumTime = 1f;
    private const float SiegeDefenderAdvantage = 2f;
    private const int MapEventSettlementSettingDistance = 3;
    [SaveableField(101)]
    private MapEventState _state;
    [SaveableField(102)]
    private MapEventSide[] _sides = new MapEventSide[2];
    public const float SiegeAdvantage = 1.5f;
    public bool DiplomaticallyFinished;
    [SaveableField(106)]
    private int _mapEventUpdateCount;
    [CachedData]
    internal MapEvent.PowerCalculationContext SimulationContext;
    [SaveableField(107)]
    private CampaignTime _nextSimulationTime;
    [SaveableField(108)]
    private CampaignTime _mapEventStartTime;
    [SaveableField(110)]
    private MapEvent.BattleTypes _mapEventType;
    [CachedData]
    private TerrainType _eventTerrainType;
    [CachedData]
    public IMapEventVisual MapEventVisual;
    [SaveableField(114)]
    private bool _isVisible;
    private bool _keepSiegeEvent;
    [SaveableField(116)]
    private bool FirstUpdateIsDone;
    [SaveableField(117)]
    private BattleState _battleState;
    private bool _isFinishCalled;
    private bool _battleResultsCalculated;
    private bool _battleResultsCommitted;
    private bool PlayerCaptured;
    private MapEventResultExplainer _battleResultExplainers;
    [SaveableField(125)]
    public float[] StrengthOfSide = new float[2];

    internal static void AutoGeneratedStaticCollectObjectsMapEvent(
      object o,
      List<object> collectedObjects)
    {
      ((MBObjectBase) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected override void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      base.AutoGeneratedInstanceCollectObjects(collectedObjects);
      collectedObjects.Add((object) this.StrengthOfSide);
      collectedObjects.Add((object) this._sides);
      CampaignTime.AutoGeneratedStaticCollectObjectsCampaignTime((object) this._nextSimulationTime, collectedObjects);
      CampaignTime.AutoGeneratedStaticCollectObjectsCampaignTime((object) this._mapEventStartTime, collectedObjects);
      collectedObjects.Add((object) this.Component);
      collectedObjects.Add((object) this.MapEventSettlement);
    }

    internal static object AutoGeneratedGetMemberValueComponent(object o)
    {
      return (object) ((MapEvent) o).Component;
    }

    internal static object AutoGeneratedGetMemberValueMapEventSettlement(object o)
    {
      return (object) ((MapEvent) o).MapEventSettlement;
    }

    internal static object AutoGeneratedGetMemberValuePosition(object o)
    {
      return (object) ((MapEvent) o).Position;
    }

    internal static object AutoGeneratedGetMemberValueIsInvulnerable(object o)
    {
      return (object) ((MapEvent) o).IsInvulnerable;
    }

    internal static object AutoGeneratedGetMemberValueIsPlayerSimulation(object o)
    {
      return (object) ((MapEvent) o).IsPlayerSimulation;
    }

    internal static object AutoGeneratedGetMemberValueStrengthOfSide(object o)
    {
      return (object) ((MapEvent) o).StrengthOfSide;
    }

    internal static object AutoGeneratedGetMemberValue_state(object o)
    {
      return (object) ((MapEvent) o)._state;
    }

    internal static object AutoGeneratedGetMemberValue_sides(object o)
    {
      return (object) ((MapEvent) o)._sides;
    }

    internal static object AutoGeneratedGetMemberValue_mapEventUpdateCount(object o)
    {
      return (object) ((MapEvent) o)._mapEventUpdateCount;
    }

    internal static object AutoGeneratedGetMemberValue_nextSimulationTime(object o)
    {
      return (object) ((MapEvent) o)._nextSimulationTime;
    }

    internal static object AutoGeneratedGetMemberValue_mapEventStartTime(object o)
    {
      return (object) ((MapEvent) o)._mapEventStartTime;
    }

    internal static object AutoGeneratedGetMemberValue_mapEventType(object o)
    {
      return (object) ((MapEvent) o)._mapEventType;
    }

    internal static object AutoGeneratedGetMemberValue_isVisible(object o)
    {
      return (object) ((MapEvent) o)._isVisible;
    }

    internal static object AutoGeneratedGetMemberValueFirstUpdateIsDone(object o)
    {
      return (object) ((MapEvent) o).FirstUpdateIsDone;
    }

    internal static object AutoGeneratedGetMemberValue_battleState(object o)
    {
      return (object) ((MapEvent) o)._battleState;
    }

    public static MapEvent PlayerMapEvent => MobileParty.MainParty?.MapEvent;

    public BattleSideEnum PlayerSide => PartyBase.MainParty.Side;

    internal IBattleObserver BattleObserver { get; set; }

    [SaveableProperty(105)]
    public MapEventComponent Component { get; private set; }

    public MapEventState State
    {
      get => this._state;
      private set
      {
        if (this._state == value)
          return;
        if (this.IsPlayerMapEvent)
          Debug.Print("Player MapEvent State: " + value.ToString());
        this._state = value;
      }
    }

    public void BeginWait() => this.State = MapEventState.Wait;

    public MapEventSide AttackerSide => this._sides[1];

    public MapEventSide DefenderSide => this._sides[0];

    public MapEventSide GetMapEventSide(BattleSideEnum side) => this._sides[(int) side];

    internal TroopRoster GetMemberRosterReceivingLootShare(PartyBase party)
    {
      return this._sides[(int) party.Side].MemberRosterForPlayerLootShare(party);
    }

    internal TroopRoster GetPrisonerRosterReceivingLootShare(PartyBase party)
    {
      return this._sides[(int) party.Side].PrisonerRosterForPlayerLootShare(party);
    }

    internal ItemRoster GetItemRosterReceivingLootShare(PartyBase party)
    {
      return this._sides[(int) party.Side].ItemRosterForPlayerLootShare(party);
    }

    public MBReadOnlyList<MapEventParty> PartiesOnSide(BattleSideEnum side)
    {
      return this._sides[(int) side].Parties;
    }

    public IEnumerable<PartyBase> InvolvedParties
    {
      get
      {
        MapEventSide[] mapEventSideArray = this._sides;
        for (int index = 0; index < mapEventSideArray.Length; ++index)
        {
          foreach (MapEventParty party in (List<MapEventParty>) mapEventSideArray[index].Parties)
            yield return party.Party;
        }
        mapEventSideArray = (MapEventSide[]) null;
      }
    }

    [SaveableProperty(103)]
    public Settlement MapEventSettlement { get; private set; }

    internal bool AttackersRanAway { get; private set; }

    public void GetBattleRewards(
      PartyBase party,
      out float renownChange,
      out float influenceChange,
      out float moraleChange,
      out float goldChange,
      out float playerEarnedLootPercentage)
    {
      renownChange = 0.0f;
      influenceChange = 0.0f;
      moraleChange = 0.0f;
      goldChange = 0.0f;
      playerEarnedLootPercentage = 0.0f;
      foreach (MapEventSide side in this._sides)
      {
        foreach (MapEventParty party1 in (List<MapEventParty>) side.Parties)
        {
          if (party == party1.Party)
          {
            renownChange = party1.GainedRenown;
            influenceChange = party1.GainedInfluence;
            moraleChange = party1.MoraleChange;
            goldChange = (float) (party1.PlunderedGold - party1.GoldLost);
            float totalContribution = (float) this.GetMapEventSide(party1.Party.Side).CalculateTotalContribution();
            playerEarnedLootPercentage = (float) (int) (100.0 * ((double) party1.ContributionToBattle / (double) totalContribution));
          }
        }
      }
    }

    [SaveableProperty(111)]
    public Vec2 Position { get; private set; }

    public MapEvent.BattleTypes EventType => this._mapEventType;

    public TerrainType EventTerrainType => this._eventTerrainType;

    [SaveableProperty(113)]
    public bool IsInvulnerable { get; set; }

    public bool IsFieldBattle => this._mapEventType == MapEvent.BattleTypes.FieldBattle;

    public bool IsRaid => this._mapEventType == MapEvent.BattleTypes.Raid;

    public bool IsForcingVolunteers
    {
      get => this._mapEventType == MapEvent.BattleTypes.IsForcingVolunteers;
    }

    public bool IsForcingSupplies => this._mapEventType == MapEvent.BattleTypes.IsForcingSupplies;

    public bool IsSiegeAssault => this._mapEventType == MapEvent.BattleTypes.Siege;

    public bool IsHideoutBattle => this._mapEventType == MapEvent.BattleTypes.Hideout;

    public bool IsSallyOut => this._mapEventType == MapEvent.BattleTypes.SallyOut;

    public bool IsSiegeOutside => this._mapEventType == MapEvent.BattleTypes.SiegeOutside;

    public bool IsSiegeAmbush => this.Component is SiegeAmbushEventComponent;

    internal MapEvent()
    {
      this.MapEventVisual = Campaign.Current.VisualCreator.CreateMapEventVisual(this);
    }

    [LateLoadInitializationCallback]
    private void OnLateLoad(MetaData metaData, ObjectLoadData objectLoadData)
    {
      if (this.Component == null && MBSaveLoad.IsUpdatingGameVersion && MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.1.0"))
      {
        if (this._mapEventType == MapEvent.BattleTypes.Raid)
          this.Component = (MapEventComponent) RaidEventComponent.CreateComponentForOldSaves(this, (float) objectLoadData.GetMemberValueBySaveId(109), (int) objectLoadData.GetMemberValueBySaveId(112), (float) objectLoadData.GetMemberValueBySaveId(115));
        else if (this._mapEventType == MapEvent.BattleTypes.IsForcingSupplies)
          this.Component = (MapEventComponent) ForceSuppliesEventComponent.CreateComponentForOldSaves(this);
        else if (this._mapEventType == MapEvent.BattleTypes.IsForcingVolunteers)
          this.Component = (MapEventComponent) ForceVolunteersEventComponent.CreateComponentForOldSaves(this);
        else if (this._mapEventType == MapEvent.BattleTypes.IsForcingVolunteers)
          this.Component = (MapEventComponent) HideoutEventComponent.CreateComponentForOldSaves(this);
        else if (this._mapEventType == MapEvent.BattleTypes.FieldBattle)
          this.Component = (MapEventComponent) FieldBattleEventComponent.CreateComponentForOldSaves(this);
      }
      this.Component?.AfterLoad(this);
    }

    internal void OnAfterLoad()
    {
      this.CacheSimulationData();
      this._eventTerrainType = Campaign.Current.MapSceneWrapper.GetFaceTerrainType(Campaign.Current.MapSceneWrapper.GetFaceIndex(this.Position));
      if (!PartyBase.IsPositionOkForTraveling(this.Position))
      {
        Vec2 mapEventPosition = this.CalculateMapEventPosition(this.AttackerSide.LeaderParty, this.DefenderSide.LeaderParty);
        if (mapEventPosition != this.Position)
        {
          Vec2 vec2 = mapEventPosition - this.Position;
          foreach (PartyBase involvedParty in this.InvolvedParties)
          {
            if (involvedParty.IsMobile && involvedParty.MobileParty.EventPositionAdder.IsNonZero())
              involvedParty.MobileParty.EventPositionAdder += vec2;
          }
          this.Position = mapEventPosition;
        }
      }
      if (!this.IsFinalized)
      {
        this.MapEventVisual = Campaign.Current.VisualCreator.CreateMapEventVisual(this);
        this.MapEventVisual.Initialize(this.Position, this.GetBattleSizeValue(), this.AttackerSide.LeaderParty != PartyBase.MainParty && this.DefenderSide.LeaderParty != PartyBase.MainParty, this.IsVisible);
      }
      if (!MBSaveLoad.IsUpdatingGameVersion || !(MBSaveLoad.LastLoadedGameVersion < ApplicationVersion.FromString("v1.2.0")))
        return;
      if (!this.AttackerSide.Parties.Any<MapEventParty>() || !this.DefenderSide.Parties.Any<MapEventParty>())
      {
        if (this.InvolvedParties.ContainsQ<PartyBase>(PlayerEncounter.EncounteredParty))
          PlayerEncounter.Finish();
        this.FinalizeEvent();
      }
      if (this.MapEventSettlement == null)
        return;
      if (this.IsRaid && this.MapEventSettlement.Party.MapEvent == null)
      {
        this.FinalizeEvent();
      }
      else
      {
        if (this.EventType != MapEvent.BattleTypes.Siege || this.MapEventSettlement.SiegeEvent != null)
          return;
        this.FinalizeEvent();
      }
    }

    internal void Initialize(
      PartyBase attackerParty,
      PartyBase defenderParty,
      MapEventComponent component = null,
      MapEvent.BattleTypes mapEventType = MapEvent.BattleTypes.None)
    {
      this.Component = component;
      this.FirstUpdateIsDone = false;
      this.AttackersRanAway = false;
      this.MapEventSettlement = (Settlement) null;
      this._mapEventType = mapEventType;
      this._mapEventUpdateCount = 0;
      this._sides[0] = new MapEventSide(this, BattleSideEnum.Defender, defenderParty);
      this._sides[1] = new MapEventSide(this, BattleSideEnum.Attacker, attackerParty);
      if (attackerParty.MobileParty == MobileParty.MainParty || defenderParty.MobileParty == MobileParty.MainParty)
      {
        if (mapEventType == MapEvent.BattleTypes.Raid)
          Debug.Print("A raid mapEvent has been started on " + (object) defenderParty.Name + "\n", color: Debug.DebugColor.DarkGreen, debugFilter: 64UL);
        else if (defenderParty.IsSettlement && defenderParty.Settlement.IsFortification)
          Debug.Print("A siege mapEvent has been started on " + (object) defenderParty.Name + "\n", color: Debug.DebugColor.DarkCyan, debugFilter: 64UL);
      }
      if (attackerParty.IsMobile && attackerParty.MobileParty.CurrentSettlement != null)
        this.MapEventSettlement = attackerParty.MobileParty.CurrentSettlement;
      else if (defenderParty.IsMobile && defenderParty.MobileParty.CurrentSettlement != null)
        this.MapEventSettlement = defenderParty.MobileParty.CurrentSettlement;
      else if ((!attackerParty.IsMobile || attackerParty.MobileParty.BesiegedSettlement == null) && defenderParty.IsMobile)
      {
        Settlement besiegedSettlement = defenderParty.MobileParty.BesiegedSettlement;
      }
      if (attackerParty.IsSettlement)
        this.MapEventSettlement = attackerParty.Settlement;
      else if (defenderParty.IsSettlement)
      {
        this.MapEventSettlement = defenderParty.Settlement;
        this.MapEventSettlement.LastAttackerParty = attackerParty.MobileParty;
      }
      if (this.IsFieldBattle)
      {
        this.MapEventSettlement = (Settlement) null;
        if (attackerParty == PartyBase.MainParty || defenderParty == PartyBase.MainParty)
        {
          Settlement nearestVillage = SettlementHelper.FindNearestVillage((Func<Settlement, bool>) (x => (double) x.Position2D.DistanceSquared(attackerParty.Position2D) < 9.0));
          if (nearestVillage != null)
            this.MapEventSettlement = nearestVillage;
        }
      }
      this.Position = this.CalculateMapEventPosition(attackerParty, defenderParty);
      this._eventTerrainType = Campaign.Current.MapSceneWrapper.GetFaceTerrainType(Campaign.Current.MapSceneWrapper.GetFaceIndex(this.Position));
      this.CacheSimulationData();
      attackerParty.MapEventSide = this.AttackerSide;
      defenderParty.MapEventSide = this.DefenderSide;
      if (this.MapEventSettlement != null && (mapEventType == MapEvent.BattleTypes.Siege || mapEventType == MapEvent.BattleTypes.SiegeOutside || mapEventType == MapEvent.BattleTypes.SallyOut || this.IsSiegeAmbush))
      {
        foreach (PartyBase partyBase in this.MapEventSettlement.SiegeEvent.BesiegerCamp.GetInvolvedPartiesForEventType(mapEventType))
        {
          if (partyBase.MapEventSide == null && (partyBase != PartyBase.MainParty || partyBase.MobileParty.Army != null) && (partyBase.MobileParty.Army == null || partyBase.MobileParty.Army.LeaderParty == partyBase.MobileParty))
            partyBase.MapEventSide = mapEventType == MapEvent.BattleTypes.SallyOut ? defenderParty.MapEventSide : attackerParty.MapEventSide;
        }
      }
      if (defenderParty.IsMobile && defenderParty.MobileParty.BesiegedSettlement != null)
      {
        List<PartyBase> partiesForEventType = defenderParty.MobileParty.SiegeEvent.GetInvolvedPartiesForEventType(this._mapEventType);
        PartyBase partyBase1 = this.IsSiegeAssault ? attackerParty : defenderParty;
        foreach (PartyBase partyBase2 in partiesForEventType)
        {
          if (partyBase2 != partyBase1 && partyBase2.IsMobile && partyBase2 != PartyBase.MainParty && partyBase2.MobileParty.BesiegedSettlement == defenderParty.MobileParty.BesiegedSettlement && (partyBase2.MobileParty.Army == null || partyBase2.MobileParty.Army.LeaderParty == partyBase2.MobileParty))
            partyBase2.MapEventSide = this.DefenderSide;
        }
      }
      this.State = MapEventState.Wait;
      this._mapEventStartTime = CampaignTime.Now;
      this._nextSimulationTime = MapEvent.CalculateNextSimulationTime();
      this.Component?.InitializeComponent();
      if (this.MapEventSettlement != null)
        this.AddInsideSettlementParties(this.MapEventSettlement);
      this.MapEventVisual.Initialize(this.Position, this.GetBattleSizeValue(), this.AttackerSide.LeaderParty != PartyBase.MainParty && this.DefenderSide.LeaderParty != PartyBase.MainParty, this.IsVisible);
      this.BattleState = BattleState.None;
      CampaignEventDispatcher.Instance.OnMapEventStarted(this, attackerParty, defenderParty);
    }

    private Vec2 CalculateMapEventPosition(PartyBase attackerParty, PartyBase defenderParty)
    {
      Vec2 mapEventPosition;
      if (defenderParty.IsSettlement)
      {
        mapEventPosition = defenderParty.Position2D;
      }
      else
      {
        mapEventPosition = attackerParty.Position2D + defenderParty.Position2D;
        mapEventPosition = new Vec2(mapEventPosition.x / 2f, mapEventPosition.y / 2f);
      }
      return mapEventPosition;
    }

    internal bool IsWinnerSide(BattleSideEnum side)
    {
      if (this.BattleState == BattleState.DefenderVictory && side == BattleSideEnum.Defender)
        return true;
      return this.BattleState == BattleState.AttackerVictory && side == BattleSideEnum.Attacker;
    }

    private void AddInsideSettlementParties(Settlement relatedSettlement)
    {
      List<PartyBase> partyBaseList = new List<PartyBase>();
      foreach (PartyBase partyBase in relatedSettlement.GetInvolvedPartiesForEventType(this._mapEventType))
      {
        if (partyBase != PartyBase.MainParty && partyBase.MobileParty?.AttachedTo != MobileParty.MainParty)
          partyBaseList.Add(partyBase);
      }
      foreach (PartyBase party in partyBaseList)
      {
        if (this.MapEventSettlement.SiegeEvent != null)
        {
          if (this.MapEventSettlement.SiegeEvent.CanPartyJoinSide(party, BattleSideEnum.Defender))
            party.MapEventSide = !this.IsSallyOut ? this.DefenderSide : this.AttackerSide;
          else if (party.MobileParty != null && !party.MobileParty.IsGarrison && !party.MobileParty.IsMilitia)
          {
            LeaveSettlementAction.ApplyForParty(party.MobileParty);
            party.MobileParty.Ai.SetMoveModeHold();
          }
        }
        else if (this.CanPartyJoinBattle(party, BattleSideEnum.Defender))
          party.MapEventSide = this.DefenderSide;
        else if (this.CanPartyJoinBattle(party, BattleSideEnum.Attacker))
          party.MapEventSide = this.AttackerSide;
        else if (party.MobileParty != null && !party.MobileParty.IsGarrison && !party.MobileParty.IsMilitia)
          LeaveSettlementAction.ApplyForParty(party.MobileParty);
      }
    }

    private int GetBattleSizeValue()
    {
      if (this.IsSiegeAssault)
        return 4;
      int numberOfInvolvedMen = this.GetNumberOfInvolvedMen();
      if (numberOfInvolvedMen < 30)
        return 0;
      if (numberOfInvolvedMen < 80)
        return 1;
      return numberOfInvolvedMen >= 120 ? 3 : 2;
    }

    private static CampaignTime CalculateNextSimulationTime()
    {
      return CampaignTime.Now + CampaignTime.Minutes(30L);
    }

    internal void AddInvolvedPartyInternal(PartyBase involvedParty, BattleSideEnum side)
    {
      if (involvedParty.LeaderHero != null && involvedParty.LeaderHero.Clan == Clan.PlayerClan && involvedParty != PartyBase.MainParty && side == BattleSideEnum.Defender && this.AttackerSide.LeaderParty != null)
      {
        bool flag = false;
        foreach (PartyBase involvedParty1 in this.InvolvedParties)
        {
          if (involvedParty1 == PartyBase.MainParty)
          {
            flag = true;
            break;
          }
        }
        if (!flag)
        {
          Settlement settlement1 = Hero.MainHero.HomeSettlement;
          float num1 = Campaign.MapDiagonalSquared;
          foreach (Settlement settlement2 in (List<Settlement>) Settlement.All)
          {
            if (settlement2.IsVillage || settlement2.IsFortification)
            {
              float num2 = settlement2.Position2D.DistanceSquared(involvedParty.Position2D);
              if ((double) num2 < (double) num1)
              {
                num1 = num2;
                settlement1 = settlement2;
              }
            }
          }
          if (settlement1 != null)
          {
            TextObject text = GameTexts.FindText("str_party_attacked");
            text.SetTextVariable("CLAN_PARTY_NAME", involvedParty.Name);
            text.SetTextVariable("ENEMY_PARTY_NAME", this.AttackerSide.LeaderParty.Name);
            text.SetTextVariable("SETTLEMENT_NAME", settlement1.Name);
            MBInformationManager.AddQuickInformation(text);
          }
        }
      }
      if (this.IsSiegeAssault && involvedParty.MobileParty != null && involvedParty.MobileParty.CurrentSettlement == null && side == BattleSideEnum.Defender)
        this._mapEventType = MapEvent.BattleTypes.SiegeOutside;
      if (involvedParty.MobileParty != null && involvedParty.MobileParty.IsGarrison && side == BattleSideEnum.Attacker && this.IsSiegeOutside)
      {
        this._mapEventType = MapEvent.BattleTypes.SallyOut;
        this.MapEventSettlement = involvedParty.MobileParty.CurrentSettlement;
      }
      involvedParty.ResetTempXp();
      if (involvedParty == MobileParty.MainParty.Party && !this.IsSiegeAssault && !this.IsRaid)
        involvedParty.MobileParty.Ai.SetMoveModeHold();
      if (involvedParty == PartyBase.MainParty)
        involvedParty.MobileParty.Ai.ForceAiNoPathMode = false;
      this.RecalculateRenownAndInfluenceValues(involvedParty);
      if (this.IsFieldBattle && involvedParty.IsMobile && involvedParty.MobileParty.BesiegedSettlement == null)
      {
        Vec2 vec2 = this.GetMapEventSide(side).LeaderParty.Position2D - this.Position;
        float a = vec2.Normalize();
        if (involvedParty != this.GetMapEventSide(side).LeaderParty)
        {
          int num = this.GetMapEventSide(side).Parties.Count<MapEventParty>((Func<MapEventParty, bool>) (p => p.Party.IsMobile)) - 1;
          involvedParty.MobileParty.EventPositionAdder = this.Position + vec2 * MathF.Max(a, 0.4f) + (float) ((num + 1) / 2 * (num % 2 == 0 ? 1 : -1)) * vec2.RightVec() * 0.4f - (involvedParty.Position2D + involvedParty.MobileParty.ArmyPositionAdder);
        }
        else
          involvedParty.MobileParty.EventPositionAdder = this.Position + vec2 * MathF.Max(a, 0.4f) - (involvedParty.Position2D + involvedParty.MobileParty.ArmyPositionAdder);
      }
      involvedParty.SetVisualAsDirty();
      if (involvedParty.IsMobile && involvedParty.MobileParty.Army != null && involvedParty.MobileParty.Army.LeaderParty == involvedParty.MobileParty)
      {
        foreach (MobileParty attachedParty in (List<MobileParty>) involvedParty.MobileParty.Army.LeaderParty.AttachedParties)
          attachedParty.Party.SetVisualAsDirty();
      }
      if (this.HasWinner && involvedParty.MapEventSide.MissionSide != this.WinningSide && involvedParty.NumberOfHealthyMembers > 0)
        this.BattleState = BattleState.None;
      if (involvedParty.IsVisible)
        this.IsVisible = true;
      this.ResetUnsuitablePartiesThatWereTargetingThisMapEvent();
    }

    public bool IsVisible
    {
      get => this._isVisible;
      private set
      {
        this._isVisible = value;
        this.MapEventVisual?.SetVisibility(value);
      }
    }

    internal void PartyVisibilityChanged(PartyBase party, bool isPartyVisible)
    {
      if (isPartyVisible)
      {
        this.IsVisible = true;
      }
      else
      {
        bool flag = false;
        foreach (PartyBase involvedParty in this.InvolvedParties)
        {
          if (involvedParty != party && involvedParty.IsVisible)
          {
            flag = true;
            break;
          }
        }
        this.IsVisible = flag;
      }
    }

    internal void RemoveInvolvedPartyInternal(PartyBase party)
    {
      party.SetVisualAsDirty();
      if (party.IsMobile && party.MobileParty.Army != null && party.MobileParty.Army.LeaderParty == party.MobileParty)
      {
        foreach (MobileParty attachedParty in (List<MobileParty>) party.MobileParty.Army.LeaderParty.AttachedParties)
          attachedParty.Party.SetVisualAsDirty();
      }
      if (this.IsFieldBattle && party.IsMobile)
      {
        party.MobileParty.EventPositionAdder = Vec2.Zero;
        foreach (MapEventSide side in this._sides)
        {
          MapEventParty[] array = side.Parties.ToArray();
          Vec2 vec2 = side.LeaderParty.Position2D - this.Position;
          float a = vec2.Normalize();
          for (int index = 0; index < array.Length; ++index)
          {
            PartyBase party1 = array[index].Party;
            if (party1.IsMobile && party1 != side.LeaderParty)
              party1.MobileParty.EventPositionAdder = this.Position + vec2 * MathF.Max(a, 0.4f) + (float) ((index + 1) / 2 * (index % 2 == 0 ? 1 : -1)) * vec2.RightVec() * 0.4f - (party1.Position2D + party1.MobileParty.ArmyPositionAdder);
          }
        }
      }
      if (this.IsSiegeOutside && (this.MapEventSettlement != null ? this.DefenderSide : this.AttackerSide).Parties.All<MapEventParty>((Func<MapEventParty, bool>) (x =>
      {
        if (x.Party.MobileParty == null)
          return true;
        return this.MapEventSettlement != null && x.Party.MobileParty.CurrentSettlement == this.MapEventSettlement;
      })) && this.MapEventSettlement != null)
        this._mapEventType = MapEvent.BattleTypes.Siege;
      if (party == PartyBase.MainParty && this.State == MapEventState.Wait)
      {
        this.AttackerSide.RemoveNearbyPartiesFromPlayerMapEvent();
        this.DefenderSide.RemoveNearbyPartiesFromPlayerMapEvent();
      }
      if (party.IsVisible)
        this.PartyVisibilityChanged(party, false);
      this.ResetUnsuitablePartiesThatWereTargetingThisMapEvent();
      if (!party.IsMobile || party.MobileParty.IsCurrentlyUsedByAQuest)
        return;
      party.MobileParty.Ai.SetMoveModeHold();
    }

    public int GetNumberOfInvolvedMen()
    {
      return this.DefenderSide.RecalculateMemberCountOfSide() + this.AttackerSide.RecalculateMemberCountOfSide();
    }

    public int GetNumberOfInvolvedMen(BattleSideEnum side)
    {
      return this.GetMapEventSide(side).RecalculateMemberCountOfSide();
    }

    private void LootDefeatedParties(out bool playerCaptured, LootCollector lootCollector)
    {
      this.GetMapEventSide(this.DefeatedSide).CollectAll(lootCollector, out playerCaptured);
    }

    internal void AddCasualtiesInBattle(TroopRoster troopRoster, LootCollector lootCollector)
    {
      lootCollector.CasualtiesInBattle.Add(troopRoster);
    }

    private int CalculatePlunderedGold()
    {
      float plunderedGold = 0.0f;
      foreach (MapEventParty party1 in (List<MapEventParty>) this.GetMapEventSide(this.DefeatedSide).Parties)
      {
        PartyBase party2 = party1.Party;
        if (party2.LeaderHero != null)
        {
          int goldLossAfterDefeat = Campaign.Current.Models.BattleRewardModel.CalculateGoldLossAfterDefeat(party2.LeaderHero);
          plunderedGold += (float) goldLossAfterDefeat;
          party1.GoldLost = goldLossAfterDefeat;
        }
        else if (party2.IsMobile && party2.MobileParty.IsPartyTradeActive)
        {
          int num = party2.MobileParty.IsBandit ? (int) ((double) party2.MobileParty.PartyTradeGold * 0.5) : (int) ((double) party2.MobileParty.PartyTradeGold * 0.10000000149011612);
          plunderedGold += (float) num;
          party1.GoldLost = num;
        }
      }
      return (int) plunderedGold;
    }

    private void CalculateRenownShares(MapEventResultExplainer resultExplainers = null, bool forScoreboard = false)
    {
      if (this.BattleState != BattleState.AttackerVictory && this.BattleState != BattleState.DefenderVictory)
        return;
      (this.BattleState == BattleState.AttackerVictory ? this.AttackerSide : this.DefenderSide).DistributeRenownAndInfluence(resultExplainers, forScoreboard);
    }

    private void CalculateLootShares(LootCollector lootCollector)
    {
      if (this.BattleState != BattleState.AttackerVictory && this.BattleState != BattleState.DefenderVictory)
        return;
      (this.BattleState == BattleState.AttackerVictory ? this.AttackerSide : this.DefenderSide).DistributeLootAmongWinners(lootCollector);
    }

    private int GetSimulatedDamage(
      CharacterObject strikerTroop,
      CharacterObject strikedTroop,
      PartyBase strikerParty,
      PartyBase strikedParty,
      float strikerAdvantage)
    {
      return Campaign.Current.Models.CombatSimulationModel.SimulateHit(strikerTroop, strikedTroop, strikerParty, strikedParty, strikerAdvantage, this);
    }

    private void SimulateBattleForRound(BattleSideEnum side, float advantage)
    {
      int strikerSideIndex = (int) side;
      if (this.AttackerSide.NumRemainingSimulationTroops != 0 && this.DefenderSide.NumRemainingSimulationTroops != 0 && !this.SimulateSingleHit(strikerSideIndex, 1 - strikerSideIndex, advantage))
        return;
      bool isRoundWinnerDetermined = false;
      BattleState calculateWinner = this.GetCalculateWinner(ref isRoundWinnerDetermined);
      if (calculateWinner != BattleState.None)
      {
        this.BattleState = calculateWinner;
      }
      else
      {
        if (!isRoundWinnerDetermined)
          return;
        this.BattleObserver?.BattleResultsReady();
      }
    }

    private bool SimulateSingleHit(
      int strikerSideIndex,
      int strikedSideIndex,
      float strikerAdvantage)
    {
      MapEventSide side1 = this._sides[strikerSideIndex];
      MapEventSide side2 = this._sides[strikedSideIndex];
      UniqueTroopDescriptor uniqueTroopDescriptor1 = side1.SelectRandomSimulationTroop();
      UniqueTroopDescriptor uniqueTroopDescriptor2 = side2.SelectRandomSimulationTroop();
      CharacterObject allocatedTroop1 = side1.GetAllocatedTroop(uniqueTroopDescriptor1);
      CharacterObject allocatedTroop2 = side2.GetAllocatedTroop(uniqueTroopDescriptor2);
      PartyBase allocatedTroopParty1 = side1.GetAllocatedTroopParty(uniqueTroopDescriptor1);
      PartyBase allocatedTroopParty2 = side2.GetAllocatedTroopParty(uniqueTroopDescriptor2);
      int damage = this.GetSimulatedDamage(allocatedTroop1, allocatedTroop2, allocatedTroopParty1, allocatedTroopParty2, strikerAdvantage);
      if (damage <= 0)
        return false;
      if (this.IsPlayerSimulation && allocatedTroopParty2 == PartyBase.MainParty)
      {
        float damageMultiplier = Campaign.Current.Models.DifficultyModel.GetPlayerTroopsReceivedDamageMultiplier();
        damage = MBRandom.RoundRandomized((float) damage * damageMultiplier);
      }
      DamageTypes damageType = (double) MBRandom.RandomFloat < 0.30000001192092896 ? DamageTypes.Blunt : DamageTypes.Cut;
      bool selectedTroop = side2.ApplySimulationDamageToSelectedTroop(damage, damageType, allocatedTroopParty1);
      side1.ApplySimulatedHitRewardToSelectedTroop(allocatedTroop1, allocatedTroop2, damage, selectedTroop);
      if (((!this.IsPlayerSimulation ? 0 : (allocatedTroopParty1 == PartyBase.MainParty ? 1 : 0)) & (selectedTroop ? 1 : 0)) != 0)
        CampaignEventDispatcher.Instance.OnPlayerPartyKnockedOrKilledTroop(allocatedTroop2);
      return selectedTroop;
    }

    private bool GetAttackersRunAwayChance()
    {
      if (this._mapEventUpdateCount <= 1 || this.AttackerSide.LeaderParty.LeaderHero == null || this.IsSallyOut)
        return false;
      float num1 = 0.0f;
      foreach (MapEventParty party in (List<MapEventParty>) this.AttackerSide.Parties)
        num1 += party.Party.TotalStrength;
      float num2 = 0.0f;
      foreach (MapEventParty party in (List<MapEventParty>) this.DefenderSide.Parties)
        num2 += party.Party.TotalStrength;
      if (this.IsSiegeAssault)
        num1 *= 0.6666667f;
      return (double) num2 > (double) num1 * 1.1000000238418579 && (double) MBRandom.RandomFloat * (this._mapEventUpdateCount < 16 ? (double) MathF.Sqrt((float) this._mapEventUpdateCount / 16f) : 1.0) > (double) num1 / ((double) num2 * 1.1000000238418579);
    }

    internal void Update()
    {
      if (this._isFinishCalled)
        return;
      bool finish = false;
      if (this._sides[0].LeaderParty == null || this._sides[1].LeaderParty == null || !this._sides[0].LeaderParty.MapFaction.IsAtWarWith(this._sides[1].LeaderParty.MapFaction))
        this.DiplomaticallyFinished = true;
      if (!this.DiplomaticallyFinished)
      {
        this.Component?.Update(ref finish);
        if ((this.DefenderSide.TroopCount > 0 && this.AttackerSide.TroopCount > 0 || !this.FirstUpdateIsDone && (this.DefenderSide.TroopCount > 0 || this._mapEventType != MapEvent.BattleTypes.Raid)) && this._nextSimulationTime.IsPast)
        {
          this.AttackersRanAway = this._mapEventType != MapEvent.BattleTypes.Siege && this._mapEventType != MapEvent.BattleTypes.SallyOut && this._mapEventType != MapEvent.BattleTypes.SiegeOutside && this._mapEventType != MapEvent.BattleTypes.Raid && this.GetAttackersRunAwayChance();
          ++this._mapEventUpdateCount;
          if (!this.AttackersRanAway)
          {
            this.SimulateBattleSessionForMapEvent();
            this._nextSimulationTime = MapEvent.CalculateNextSimulationTime();
            this.FirstUpdateIsDone = true;
          }
          else
            finish = true;
        }
        if ((this._mapEventType != MapEvent.BattleTypes.Raid || this.DefenderSide.Parties.Count > 1) && this.BattleState != BattleState.None)
          finish = true;
      }
      else
      {
        finish = true;
        foreach (PartyBase involvedParty in this.InvolvedParties)
        {
          if (involvedParty.IsMobile && involvedParty.MobileParty != MobileParty.MainParty && (involvedParty.MobileParty.Army == null || involvedParty.MobileParty.Army.LeaderParty == involvedParty.MobileParty))
            involvedParty.MobileParty.Ai.RecalculateShortTermAi();
        }
      }
      if (!finish)
        return;
      this.Component?.Finish();
      if (this.IsPlayerMapEvent && PlayerEncounter.Current != null)
        return;
      this.FinishBattle();
    }

    public void FinishBattleAndKeepSiegeEvent()
    {
      this._keepSiegeEvent = true;
      this.FinishBattle();
    }

    private void CheckSiegeStageChange()
    {
      if (this.MapEventSettlement == null || !this.IsSiegeAssault)
        return;
      int num = this.AttackerSide.Parties.Sum<MapEventParty>((Func<MapEventParty, int>) (party => party.Party.NumberOfHealthyMembers));
      this.DefenderSide.Parties.Sum<MapEventParty>((Func<MapEventParty, int>) (party => party.Party.NumberOfHealthyMembers));
      if (num == 0)
        ;
    }

    public void SimulateBattleSetup(FlattenedTroopRoster[] priorTroops)
    {
      if (this.IsSiegeAssault)
        this.CheckSiegeStageChange();
      foreach (MapEventSide side in this._sides)
      {
        FlattenedTroopRoster priorTroop = priorTroops?[(int) side.MissionSide];
        side.MakeReadyForSimulation(priorTroop, priorTroop != null ? priorTroop.Count<FlattenedTroopRosterElement>() : -1);
      }
      this._battleState = BattleState.None;
    }

    public void SimulateBattleForRounds(int simulationRoundsDefender, int simulationRoundsAttacker)
    {
      bool isRoundWinnerDetermined = false;
      this.BattleState = this.GetCalculateWinner(ref isRoundWinnerDetermined);
      (float num1, float num2) = Campaign.Current.Models.CombatSimulationModel.GetBattleAdvantage(this.DefenderSide.LeaderParty, this.AttackerSide.LeaderParty, this._mapEventType, this.MapEventSettlement);
      int num3 = 0;
      while (0 < simulationRoundsAttacker + simulationRoundsDefender && this.BattleState == BattleState.None)
      {
        if ((double) MBRandom.RandomFloat < (double) ((float) simulationRoundsAttacker / (float) (simulationRoundsAttacker + simulationRoundsDefender)))
        {
          --simulationRoundsAttacker;
          this.SimulateBattleForRound(BattleSideEnum.Attacker, num2);
        }
        else
        {
          --simulationRoundsDefender;
          this.SimulateBattleForRound(BattleSideEnum.Defender, num1);
        }
        ++num3;
      }
    }

    private void SimulateBattleSessionForMapEvent()
    {
      this.SimulateBattleSetup((FlattenedTroopRoster[]) null);
      (int defenderRounds, int attackerRounds) simulationRoundsForBattle = Campaign.Current.Models.CombatSimulationModel.GetSimulationRoundsForBattle(this, this.DefenderSide.NumRemainingSimulationTroops, this.AttackerSide.NumRemainingSimulationTroops);
      this.SimulateBattleForRounds(simulationRoundsForBattle.defenderRounds, simulationRoundsForBattle.attackerRounds);
      this.SimulateBattleEndSession();
    }

    internal void SimulatePlayerEncounterBattle()
    {
      (int defenderRounds, int attackerRounds) simulationRoundsForBattle = Campaign.Current.Models.CombatSimulationModel.GetSimulationRoundsForBattle(this, this.DefenderSide.NumRemainingSimulationTroops, this.AttackerSide.NumRemainingSimulationTroops);
      this.SimulateBattleForRounds(simulationRoundsForBattle.defenderRounds, simulationRoundsForBattle.attackerRounds);
    }

    private void SimulateBattleEndSession()
    {
      this.CommitXpGains();
      this.ApplyRenownAndInfluenceChanges();
      this.ApplyRewardsAndChanges();
      foreach (MapEventSide side in this._sides)
        side.EndSimulation();
    }

    public bool IsPlayerMapEvent => this == MapEvent.PlayerMapEvent;

    public bool IsFinished => this._state == MapEventState.WaitingRemoval;

    public BattleState BattleState
    {
      get => this._battleState;
      internal set
      {
        if (value == this._battleState)
          return;
        if (this.IsPlayerMapEvent)
          Debug.Print("Player MapEvent BattleState: " + value.ToString());
        this._battleState = value;
        if (this._battleState != BattleState.AttackerVictory && this._battleState != BattleState.DefenderVictory)
          return;
        this.OnBattleWon(this._battleState);
      }
    }

    public MapEventSide Winner
    {
      get
      {
        if (this.BattleState == BattleState.AttackerVictory)
          return this.AttackerSide;
        return this.BattleState != BattleState.DefenderVictory ? (MapEventSide) null : this.DefenderSide;
      }
    }

    private void OnBattleWon(BattleState winnerSide)
    {
      this.CalculateBattleResults(true);
      this.BattleObserver?.BattleResultsReady();
    }

    public BattleSideEnum WinningSide
    {
      get
      {
        if (this.BattleState == BattleState.AttackerVictory)
          return BattleSideEnum.Attacker;
        return this.BattleState != BattleState.DefenderVictory ? BattleSideEnum.None : BattleSideEnum.Defender;
      }
    }

    public BattleSideEnum DefeatedSide
    {
      get
      {
        if (this.BattleState == BattleState.AttackerVictory)
          return BattleSideEnum.Defender;
        return this.BattleState != BattleState.DefenderVictory ? BattleSideEnum.None : BattleSideEnum.Attacker;
      }
    }

    private BattleState GetCalculateWinner(ref bool isRoundWinnerDetermined)
    {
      BattleState calculateWinner = BattleState.None;
      int simulationTroops1 = this.AttackerSide.NumRemainingSimulationTroops;
      int simulationTroops2 = this.DefenderSide.NumRemainingSimulationTroops;
      if (this.IsPlayerSimulation && !Hero.MainHero.IsWounded && this.InvolvedParties.Contains<PartyBase>(PartyBase.MainParty))
      {
        if (PartyBase.MainParty.Side == BattleSideEnum.Attacker)
        {
          if (simulationTroops1 == 0)
            isRoundWinnerDetermined = true;
          ++simulationTroops1;
        }
        else if (PartyBase.MainParty.Side == BattleSideEnum.Defender)
        {
          if (simulationTroops2 == 0)
            isRoundWinnerDetermined = true;
          ++simulationTroops2;
        }
      }
      if (simulationTroops1 == 0)
        calculateWinner = BattleState.DefenderVictory;
      else if (simulationTroops2 == 0)
        calculateWinner = BattleState.AttackerVictory;
      return calculateWinner;
    }

    public void SetOverrideWinner(BattleSideEnum winner)
    {
      int num;
      switch (winner)
      {
        case BattleSideEnum.Defender:
          num = 1;
          break;
        case BattleSideEnum.Attacker:
          num = 2;
          break;
        default:
          num = 0;
          break;
      }
      this.BattleState = (BattleState) num;
    }

    public void SetDefenderPulledBack() => this.BattleState = BattleState.DefenderPullBack;

    public void ResetBattleState() => this.BattleState = BattleState.None;

    internal bool CheckIfOneSideHasLost()
    {
      int num1 = this.DefenderSide.RecalculateMemberCountOfSide();
      int num2 = this.AttackerSide.RecalculateMemberCountOfSide();
      if (this.BattleState == BattleState.None && (num1 == 0 || num2 == 0))
        this.BattleState = num2 > 0 ? BattleState.AttackerVictory : BattleState.DefenderVictory;
      return this.BattleState == BattleState.AttackerVictory || this.BattleState == BattleState.DefenderVictory;
    }

    internal ItemRoster ItemRosterForPlayerLootShare(PartyBase party)
    {
      return this.GetMapEventSide(party.Side).ItemRosterForPlayerLootShare(party);
    }

    public bool IsPlayerSergeant()
    {
      return this.IsPlayerMapEvent && this.GetLeaderParty(this.PlayerSide) != PartyBase.MainParty && MobileParty.MainParty.Army != null && MobileParty.MainParty.Army.LeaderParty != MobileParty.MainParty;
    }

    private void FinishBattle()
    {
      List<MobileParty> mobilePartyList = new List<MobileParty>();
      if (this.AttackersRanAway)
      {
        foreach (MapEventParty party in (List<MapEventParty>) this.AttackerSide.Parties)
        {
          if (party.Party.IsMobile)
            mobilePartyList.Add(party.Party.MobileParty);
        }
      }
      this._isFinishCalled = true;
      if (!this._battleResultsCalculated)
        this.CalculateBattleResults();
      this.ApplyBattleResults();
      this.FinalizeEventAux();
      if (!this.AttackersRanAway)
        return;
      foreach (MobileParty mobileParty in mobilePartyList)
      {
        if (mobileParty.IsActive && mobileParty.AttachedTo == null)
        {
          if (mobileParty.BesiegerCamp != null)
            mobileParty.BesiegerCamp = (BesiegerCamp) null;
          mobileParty.TeleportPartyToSafePosition();
          mobileParty.Ai.SetMoveModeHold();
        }
      }
    }

    public MapEventResultExplainer BattleResultExplainers => this._battleResultExplainers;

    public override string ToString()
    {
      return "Battle: " + (object) this.AttackerSide.LeaderParty?.Name + " x " + (object) this.DefenderSide.LeaderParty.Name;
    }

    internal void CalculateBattleResults(bool forScoreBoard = false)
    {
      if (this._battleResultsCalculated)
        return;
      this._battleResultsCalculated = !forScoreBoard;
      LootCollector lootCollector = new LootCollector();
      if (this.IsPlayerMapEvent)
      {
        this._battleResultExplainers = new MapEventResultExplainer();
        if (PlayerEncounter.EncounteredPartySurrendered)
          this._sides[(int) this.DefeatedSide.GetOppositeSide()].ResetContributionToBattleToStrength();
      }
      if (this.BattleState != BattleState.AttackerVictory && this.BattleState != BattleState.DefenderVictory)
        return;
      int plunderedGold = this.CalculatePlunderedGold();
      if (!forScoreBoard)
      {
        this.LootDefeatedParties(out this.PlayerCaptured, lootCollector);
        this.CalculatePlunderedGoldShares((float) plunderedGold, this._battleResultExplainers);
      }
      if (!forScoreBoard)
        this.CalculateLootShares(lootCollector);
      this.CalculateRenownShares(this._battleResultExplainers, forScoreBoard);
    }

    private void CalculatePlunderedGoldShares(
      float totalPlunderedGold,
      MapEventResultExplainer resultExplainers = null)
    {
      if (this.BattleState != BattleState.AttackerVictory && this.BattleState != BattleState.DefenderVictory)
        return;
      (this.BattleState == BattleState.AttackerVictory ? this.AttackerSide : this.DefenderSide).CalculatePlunderedGoldShare(totalPlunderedGold, resultExplainers);
    }

    internal void ApplyBattleResults()
    {
      if (this._battleResultsCommitted)
        return;
      this.CommitXpGains();
      this.ApplyRenownAndInfluenceChanges();
      this.ApplyRewardsAndChanges();
      this._battleResultsCommitted = true;
    }

    private void ApplyRewardsAndChanges()
    {
      foreach (MapEventSide side in this._sides)
        side.ApplyFinalRewardsAndChanges();
    }

    internal void ApplyRenownAndInfluenceChanges()
    {
      foreach (MapEventSide side in this._sides)
        side.ApplyRenownAndInfluenceChanges();
    }

    private void CommitXpGains()
    {
      foreach (MapEventSide side in this._sides)
        side.CommitXpGains();
    }

    internal void ResetBattleResults() => this._battleResultsCommitted = false;

    public bool IsFinalized => this._state == MapEventState.WaitingRemoval;

    public void FinalizeEvent() => this.FinalizeEventAux();

    private void FinalizeEventAux()
    {
      if (this.IsFinalized)
        return;
      this.State = MapEventState.WaitingRemoval;
      CampaignEventDispatcher.Instance.OnMapEventEnded(this);
      bool flag = false;
      if (this.MapEventSettlement != null)
      {
        if ((this.IsSiegeAssault || this.IsSiegeOutside || this.IsSallyOut) && this.MapEventSettlement.SiegeEvent != null)
          this.MapEventSettlement.SiegeEvent.OnBeforeSiegeEventEnd(this.BattleState, this._mapEventType);
        if (!this._keepSiegeEvent && (this.IsSiegeAssault || this.IsSiegeOutside))
        {
          switch (this.BattleState)
          {
            case BattleState.DefenderVictory:
              this.MapEventSettlement.SiegeEvent?.BesiegerCamp.RemoveAllSiegeParties();
              CampaignEventDispatcher.Instance.SiegeCompleted(this.MapEventSettlement, this.AttackerSide.LeaderParty.MobileParty, false, this._mapEventType);
              break;
            case BattleState.AttackerVictory:
              CampaignEventDispatcher.Instance.SiegeCompleted(this.MapEventSettlement, this.AttackerSide.LeaderParty.MobileParty, true, this._mapEventType);
              flag = true;
              break;
          }
        }
        else if (this.IsSallyOut && this.MapEventSettlement.Town != null && this.MapEventSettlement.Town.GarrisonParty != null && this.MapEventSettlement.Town.GarrisonParty.IsActive)
          this.MapEventSettlement.Town.GarrisonParty.Ai.SetMoveModeHold();
        this.Component?.FinalizeComponent();
      }
      foreach (MapEventSide side in this._sides)
      {
        side.UpdatePartiesMoveState();
        side.HandleMapEventEnd();
      }
      this.MapEventVisual?.OnMapEventEnd();
      foreach (PartyBase involvedParty in this.InvolvedParties)
      {
        if (involvedParty.IsMobile)
          involvedParty.MobileParty.EventPositionAdder = Vec2.Zero;
        involvedParty.SetVisualAsDirty();
        if (involvedParty.IsMobile && involvedParty.MobileParty.Army != null && involvedParty.MobileParty.Army.LeaderParty == involvedParty.MobileParty)
        {
          foreach (MobileParty attachedParty in (List<MobileParty>) involvedParty.MobileParty.Army.LeaderParty.AttachedParties)
            attachedParty.Party.SetVisualAsDirty();
        }
      }
      if (this._mapEventType != MapEvent.BattleTypes.Siege && this._mapEventType != MapEvent.BattleTypes.SiegeOutside && this._mapEventType != MapEvent.BattleTypes.SallyOut)
      {
        foreach (PartyBase involvedParty in this.InvolvedParties)
        {
          if (involvedParty.IsMobile && involvedParty != PartyBase.MainParty && involvedParty.MobileParty.BesiegedSettlement != null && (involvedParty.MobileParty.Army == null || involvedParty.MobileParty.Army.LeaderParty == involvedParty.MobileParty))
          {
            if (involvedParty.IsActive)
              EncounterManager.StartSettlementEncounter(involvedParty.MobileParty, involvedParty.MobileParty.BesiegedSettlement);
            else
              involvedParty.MobileParty.BesiegerCamp = (BesiegerCamp) null;
          }
        }
      }
      if (flag)
        this.MapEventSettlement.Militia += (float) Campaign.Current.Models.SettlementMilitiaModel.MilitiaToSpawnAfterSiege(this.MapEventSettlement.Town);
      foreach (MapEventSide side in this._sides)
        side.Clear();
    }

    public CampaignTime BattleStartTime => this._mapEventStartTime;

    public bool HasWinner
    {
      get
      {
        return this.BattleState == BattleState.AttackerVictory || this.BattleState == BattleState.DefenderVictory;
      }
    }

    [SaveableProperty(123)]
    public bool IsPlayerSimulation { get; set; }

    public bool HasTroopsOnBothSides()
    {
      return this.PartiesOnSide(BattleSideEnum.Attacker).Any<MapEventParty>((Func<MapEventParty, bool>) (party => party.Party.NumberOfHealthyMembers > 0)) & this.PartiesOnSide(BattleSideEnum.Defender).Any<MapEventParty>((Func<MapEventParty, bool>) (party => party.Party.NumberOfHealthyMembers > 0));
    }

    public PartyBase GetLeaderParty(BattleSideEnum side) => this._sides[(int) side].LeaderParty;

    public float GetRenownValue(BattleSideEnum side) => this._sides[(int) side].RenownValue;

    public float GetRenownValueAtMapEventEnd(BattleSideEnum side)
    {
      return this._sides[(int) side].RenownAtMapEventEnd;
    }

    public void RecalculateRenownAndInfluenceValues(PartyBase party)
    {
      this.StrengthOfSide[(int) party.Side] += party.TotalStrength;
      foreach (MapEventSide side in this._sides)
        side.CalculateRenownAndInfluenceValues(this.StrengthOfSide);
    }

    public void RecalculateStrengthOfSides()
    {
      foreach (MapEventSide side in this._sides)
        this.StrengthOfSide[(int) side.MissionSide] = side.RecalculateStrengthOfSide();
    }

    public void DoSurrender(BattleSideEnum side)
    {
      this.GetMapEventSide(side).Surrender();
      this.BattleState = side == BattleSideEnum.Defender ? BattleState.AttackerVictory : BattleState.DefenderVictory;
    }

    internal BattleSideEnum GetOtherSide(BattleSideEnum side)
    {
      return side != BattleSideEnum.Attacker ? BattleSideEnum.Attacker : BattleSideEnum.Defender;
    }

    private void ResetUnsuitablePartiesThatWereTargetingThisMapEvent()
    {
      LocatableSearchData<MobileParty> data = MobileParty.StartFindingLocatablesAroundPosition(this.Position, 15f);
      for (MobileParty nextLocatable = MobileParty.FindNextLocatable(ref data); nextLocatable != null; nextLocatable = MobileParty.FindNextLocatable(ref data))
      {
        if (!nextLocatable.IsMainParty && nextLocatable.ShortTermBehavior == AiBehavior.EngageParty && (nextLocatable.ShortTermTargetParty == this.GetLeaderParty(BattleSideEnum.Attacker).MobileParty || nextLocatable.ShortTermTargetParty == this.GetLeaderParty(BattleSideEnum.Defender).MobileParty) && !this.CanPartyJoinBattle(nextLocatable.Party, BattleSideEnum.Attacker) && !this.CanPartyJoinBattle(nextLocatable.Party, BattleSideEnum.Defender))
          nextLocatable.Ai.SetMoveModeHold();
      }
    }

    private void CacheSimulationData()
    {
      this._sides[0].CacheLeaderSimulationModifier();
      this._sides[1].CacheLeaderSimulationModifier();
      this.SimulationContext = this.DetermineContext();
    }

    private MapEvent.PowerCalculationContext DetermineContext()
    {
      MapEvent.PowerCalculationContext context = MapEvent.PowerCalculationContext.Default;
      switch (Campaign.Current.Models.MapWeatherModel.GetWeatherEventInPosition(this.Position))
      {
        case MapWeatherModel.WeatherEvent.Snowy:
        case MapWeatherModel.WeatherEvent.Blizzard:
          context = MapEvent.PowerCalculationContext.SnowBattle;
          break;
      }
      switch (this.EventType)
      {
        case MapEvent.BattleTypes.FieldBattle:
        case MapEvent.BattleTypes.SallyOut:
        case MapEvent.BattleTypes.SiegeOutside:
          switch (this.EventTerrainType)
          {
            case TerrainType.Water:
            case TerrainType.Swamp:
            case TerrainType.Bridge:
            case TerrainType.River:
            case TerrainType.Fording:
            case TerrainType.Lake:
              context = MapEvent.PowerCalculationContext.RiverCrossingBattle;
              break;
            case TerrainType.Steppe:
              context = MapEvent.PowerCalculationContext.SteppeBattle;
              break;
            case TerrainType.Plain:
              context = MapEvent.PowerCalculationContext.PlainBattle;
              break;
            case TerrainType.Desert:
              context = MapEvent.PowerCalculationContext.DesertBattle;
              break;
            case TerrainType.Dune:
              context = MapEvent.PowerCalculationContext.DuneBattle;
              break;
            case TerrainType.Forest:
              context = MapEvent.PowerCalculationContext.ForestBattle;
              break;
          }
          break;
        case MapEvent.BattleTypes.Raid:
        case MapEvent.BattleTypes.IsForcingVolunteers:
        case MapEvent.BattleTypes.IsForcingSupplies:
          context = MapEvent.PowerCalculationContext.Village;
          break;
        case MapEvent.BattleTypes.Siege:
          context = MapEvent.PowerCalculationContext.Siege;
          break;
      }
      return context;
    }

    Vec2 IMapEntity.InteractionPosition => this.Position;

    TextObject IMapEntity.Name => this.GetName();

    bool IMapEntity.IsMobileEntity => false;

    bool IMapEntity.ShowCircleAroundEntity => false;

    bool IMapEntity.OnMapClick(bool followModifierUsed) => false;

    void IMapEntity.OnOpenEncyclopedia()
    {
    }

    void IMapEntity.OnHover() => InformationManager.ShowTooltip(typeof (MapEvent), (object) this);

    bool IMapEntity.IsEnemyOf(IFaction faction) => false;

    bool IMapEntity.IsAllyOf(IFaction faction) => false;

    public void GetMountAndHarnessVisualIdsForPartyIcon(
      out string mountStringId,
      out string harnessStringId)
    {
      mountStringId = "";
      harnessStringId = "";
    }

    void IMapEntity.OnPartyInteraction(MobileParty mobileParty)
    {
    }

    public bool CanPartyJoinBattle(PartyBase party, BattleSideEnum side)
    {
      return this.GetMapEventSide(side).Parties.All<MapEventParty>((Func<MapEventParty, bool>) (x => !x.Party.MapFaction.IsAtWarWith(party.MapFaction))) && this.GetMapEventSide(this.GetOtherSide(side)).Parties.All<MapEventParty>((Func<MapEventParty, bool>) (x => x.Party.MapFaction.IsAtWarWith(party.MapFaction)));
    }

    public void GetStrengthsRelativeToParty(
      BattleSideEnum partySide,
      out float partySideStrength,
      out float opposingSideStrength)
    {
      partySideStrength = 0.1f;
      opposingSideStrength = 0.1f;
      if (this != null)
      {
        foreach (PartyBase involvedParty in this.InvolvedParties)
        {
          if (involvedParty.Side == partySide)
            partySideStrength += involvedParty.TotalStrength;
          else
            opposingSideStrength += involvedParty.TotalStrength;
        }
      }
      else
        Debug.FailedAssert("Cannot retrieve party strengths. MapEvent parameter is null.", "C:\\Develop\\MB3\\Source\\Bannerlord\\TaleWorlds.CampaignSystem\\MapEvents\\MapEvent.cs", nameof (GetStrengthsRelativeToParty), 1937);
    }

    public bool CheckIfBattleShouldContinueAfterBattleMission(
      CampaignBattleResult campaignBattleResult)
    {
      if (PlayerEncounter.PlayerSurrender || campaignBattleResult == null || campaignBattleResult.EnemyRetreated)
        return false;
      bool flag1 = this.IsSiegeAssault && this.BattleState == BattleState.AttackerVictory;
      MapEventSide mapEventSide = this.GetMapEventSide(this.PlayerSide);
      bool flag2 = campaignBattleResult.PlayerDefeat && mapEventSide.GetTotalHealthyTroopCountOfSide() >= 1 || (campaignBattleResult.PlayerVictory || campaignBattleResult.EnemyPulledBack) && this.DefeatedSide != BattleSideEnum.None && this.GetMapEventSide(this.DefeatedSide).GetTotalHealthyTroopCountOfSide() >= 1;
      return ((this.IsHideoutBattle ? 0 : (!flag1 ? 1 : 0)) & (flag2 ? 1 : 0)) != 0 && !mapEventSide.IsSurrendered;
    }

    public enum BattleTypes
    {
      None,
      FieldBattle,
      Raid,
      IsForcingVolunteers,
      IsForcingSupplies,
      Siege,
      Hideout,
      SallyOut,
      SiegeOutside,
    }

    public enum PowerCalculationContext
    {
      Default,
      PlainBattle,
      SteppeBattle,
      DesertBattle,
      DuneBattle,
      SnowBattle,
      ForestBattle,
      RiverCrossingBattle,
      Village,
      Siege,
    }
  }
}
